"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const m_base_1 = tslib_1.__importDefault(require("../utils/m-base"));
/**Swagger 解析器 */
class MParser {
    /**顶级参数 */
    _parm = {};
    /**分好组的Swagger Methods */
    _swgMethods = [];
    /**所有的definintions */
    _swgDefines = [];
    /**解析后的请求方法 */
    _methods = [];
    /**解析后的枚举 */
    _enums = [];
    /**解析后的模型 */
    _models = [];
    /**顶级参数 */
    get parm() {
        return this._parm;
    }
    /**计算参数中的swagger数据 */
    get swg() {
        return this.parm.swgJson;
    }
    /**计算swagger的版本 */
    get ver() {
        const { swagger, openapi } = this.swg;
        const v = swagger || openapi || "";
        return parseInt(v.split(".")[0]) || 0;
    }
    /**计算swagger请求定义数据(分组) */
    get swgMethods() {
        return this._swgMethods;
    }
    /**计算swagger模型定义数据 */
    get swgDefines() {
        return this._swgDefines;
    }
    /**计算解析后的请求方法 */
    get methods() {
        return this._methods;
    }
    /**计算解析后的枚举 */
    get enums() {
        return this._enums;
    }
    /**计算解析后的模型 */
    get models() {
        return this._models;
    }
    constructor(parm) {
        this._parm = parm;
        this._swgMethods = this.getSwaggerMethods();
        this._swgDefines = this.getSwaggerDefines();
        this.parseSwagger();
        this.fixEnumTypes();
    }
    /**获取解析结果 */
    parse() {
        const enums = this._enums;
        const models = this._models;
        const methods = this._methods;
        return { enums, models, methods };
    }
    /**解析开始 */
    parseSwagger() {
        const { swgDefines, swgMethods } = this;
        const mModels = swgDefines.map((x) => this.toModel(x));
        const mEnums = mModels.filter((x) => x.isEnum);
        const mDtos = mModels.filter((x) => !x.isEnum);
        this._enums = mEnums.map((x) => this.toParsedEnum(x));
        this._models = mDtos.map((x) => this.toParsedModel(x));
        this._methods = swgMethods.map((x) => this.toParsedMethod(x));
    }
    /**swg函数组转成ParsedMethod模型 */
    toParsedMethod(method) {
        const base = this.parm.apiName;
        const group = method.key;
        const items = method.items || [];
        const funcs = items.map((x) => this.toReqMethod(x));
        const urls = funcs.map((x) => ({
            name: x.urlName,
            value: x.path,
            description: x.description,
        }));
        const parms = funcs
            .map((x) => x.inputParms)
            .flat()
            .map((x) => x.type)
            .flat();
        const input = funcs.map((x) => x.inputModel).flat();
        const output = funcs.map((x) => x.outputModel).flat();
        const types = [...parms, ...input, ...output].distinct("type");
        const uses = this.getUsedTypes(types);
        return { base, group, uses, urls, funcs };
    }
    /**swg请求方法转成reqMethod模型 */
    toReqMethod(parm) {
        const { method, path, summary } = parm;
        const { parameters, requestBody, responses } = parm;
        const name = this.getMethodName(parm);
        const urlName = `url${m_base_1.default.camelUpperCase(name)}`;
        const description = summary || path || "";
        const inputParms = this.getMethodParms(parameters);
        const inputModel = this.getMethodBody(requestBody || parameters);
        const outputModel = this.getMethodResponse(responses["200"]);
        const mOpt1 = { name, description, path, urlName, method };
        const mOpt2 = { inputParms, inputModel, outputModel };
        return { ...mOpt1, ...mOpt2 };
    }
    /**定义模型转中间模型 */
    toModel(parm) {
        const type = parm.type;
        const enumList = parm.enum;
        const requireds = parm.required || [];
        const properties = parm.properties || {};
        const name = this.getBaseGenericName(parm.name);
        const mDesc = parm.description || name;
        if (enumList) {
            const props = enumList.map((e) => ({
                name: `${e}`,
                type: [{ type, isEnum: false }],
            }));
            return { name, props, isEnum: true, description: mDesc };
        }
        else {
            const props = [];
            for (const p in properties) {
                const pName = m_base_1.default.camelLowercase(p);
                const property = properties[p];
                const nullable = property.nullable === false ? false : true;
                const description = property.description || "";
                const required = requireds.includes(p);
                const type = this.getSchemaTypes(property);
                props.push({ name: pName, description, required, nullable, type });
            }
            return { name, props, isEnum: false, description: mDesc };
        }
    }
    /**swg中间模型转成最终模型 */
    toParsedModel(model) {
        const { name, description, props } = model;
        const types = props
            .map((p) => p.type)
            .flat()
            .distinct("type");
        let uses = this.getUsedTypes(types);
        uses = uses.filter((x) => x.name != name);
        return { name, description, props, uses };
    }
    /**swg中间模型转成最终枚举 */
    toParsedEnum(model) {
        const name = model.name;
        const description = model.description || "";
        const ps = model.props || [];
        const type = (((ps[0] || {}).type || [])[0] || {}).type || "string";
        const nums = "int,integer,int32,int64,short,long,double,decimal,float".split(",");
        const isNum = nums.includes(type);
        const props = ps.map((p) => {
            const key = isNum ? `v${p.name}` : p.name;
            const value = isNum ? parseFloat(p.name) : p.name;
            return { key, value };
        });
        return { name, type, description, props };
    }
    /**获取schema对应的模型链 */
    getSchemaTypes(parm) {
        const { swgDefines = [] } = this;
        const { type = "", items, additionalProperties } = parm;
        const { format, schema, $ref, properties, nullable } = parm;
        const description = parm.description || "";
        const pres = "#/definitions/,#/components/schemas/".split(",");
        if ($ref) {
            //优先判是否是引用类型
            let ref = $ref;
            pres.forEach((x) => (ref = ref.replace(x, "")));
            const res = swgDefines.find((d) => d.name === ref);
            if (!res)
                return [];
            const type = this.getBaseGenericName(res.name);
            return [{ type, nullable, description }];
        }
        else if (type === "array") {
            //再判是否列表类型
            return [{ type, description }, ...this.getSchemaTypes(items || {})];
        }
        else if (additionalProperties) {
            //再判是否含有附加属性类型
            return [{ type: "Map", description }];
        }
        else if (schema) {
            //再判是否含有子链类型
            return this.getSchemaTypes(schema);
        }
        else if (properties) {
            //再判是否有属性枚举
            return [{ type, description }];
        }
        else {
            //最后默认是基础类型
            return [{ type, format, nullable, description }];
        }
    }
    /**获取swagger数据中的请求(分组)列表 */
    getSwaggerMethods() {
        const paths = (this.swg.paths || {});
        const mMethods = [];
        for (const p in paths) {
            const path = paths[p];
            for (const m in path) {
                const method = path[m];
                method.path = p;
                method.method = m;
                method.group = (method.tags || [])[0];
                mMethods.push(method);
            }
        }
        return mMethods.group("group");
    }
    /**获取swagger数据中的模型定义数据 */
    getSwaggerDefines() {
        const { swg } = this;
        const { schemas } = swg.components || { schemas: swg.definitions };
        const result = [];
        if (!schemas)
            return result;
        for (const p in schemas) {
            const define = schemas[p];
            define.name = p;
            result.push(define);
        }
        return result;
    }
    /**获取请求方法名称 */
    getMethodName(parm) {
        const pre = parm.method || "get";
        const actName = m_base_1.default.camelUpperCase(parm.operationId || "");
        const pres = "/api/services/app/,/api/".split(",");
        let path = parm.path;
        //去掉api前缀
        for (const p of pres)
            path = path.replace(new RegExp(`^${p}`, "i"), "");
        //去掉path中的参数
        let paths = path.split("/");
        paths = paths.map((x) => x.replace(/{([^}]+)}/, m_base_1.default.camelUpperCase("$1")));
        paths = paths.map((x) => m_base_1.default.camelUpperCase(x));
        //如果没有操作名，用operationId补全
        if (paths.length < 2)
            paths.push(actName);
        const res = m_base_1.default.snakeToCamel(`${pre}${paths.join("")}`);
        return m_base_1.default.camelLowercase(res);
    }
    /**获取请求方法参数 */
    getMethodParms(parms) {
        const ps = parms || [];
        const result = [];
        if (ps.length < 1)
            return result;
        const mParms = ps.filter((x) => x.in != "body" && x.in != "formData");
        for (const p of mParms) {
            const { description, required, nullable } = p;
            const name = m_base_1.default.camelLowercase(p.name || "");
            const inPath = p.in == "path";
            const type = this.getSchemaTypes(p);
            result.push({ name, type, inPath, description, required, nullable });
        }
        return result;
    }
    /**获取请求方法模型链 */
    getMethodBody(body) {
        if (!body)
            return [];
        if (body instanceof Array)
            return this.getMethodBodyByParms(body);
        return this.getMethodBodyByBody(body);
    }
    /**从requestBody中获取模型链 */
    getMethodBodyByBody(body) {
        const content = body.content || {};
        const formData = content["multipart/form-data"];
        const json = content["application/*+json"] ||
            content["application/json"] ||
            content["text/json"];
        if (formData)
            return this.getSchemaTypes(formData);
        if (json)
            return this.getSchemaTypes(json);
        return [];
    }
    /**从parameters中获取模型链 */
    getMethodBodyByParms(parms) {
        const mForm = parms.filter((x) => x.in == "formData");
        const mBody = parms.filter((x) => x.in == "body");
        if (mForm.length > 0) {
            return [{ type: "Map", description: "" }];
        }
        else if (mBody.length > 0) {
            return this.getSchemaTypes(mBody[0]);
        }
        else {
            return [];
        }
    }
    /**获取请求方法响应 */
    getMethodResponse(response) {
        if (!response)
            return [];
        const content = response.content || {};
        const schema = response.schema || {};
        const res = content["application/*+json"] ||
            content["application/json"] ||
            content["text/json"] ||
            schema;
        return this.getSchemaTypes(res);
    }
    /**获取泛型基础组合名称 */
    getBaseGenericName(name) {
        if (!name)
            return "";
        const matchFull = name.match(/^(.+?)(`\d+)\[\[(.+)\]\]$/);
        const matchBase = name.match(/^(.+?)(`\d+)?\[(.+)\]$/);
        if (!matchFull && !matchBase)
            return name.split(".").pop() || name;
        const [all, fullBaseName, dDount, genericArgs] = matchFull || matchBase;
        const baseName = fullBaseName.split(".").pop() || fullBaseName;
        const args = genericArgs
            .split("],[")
            .map((x) => x.replace(/^\[|\]$/g, ""))
            .map((x) => {
            const [fullTypeName] = x.replace(/\s+/, "").split(",");
            return fullTypeName.split(".").pop() || fullTypeName;
        });
        return `${baseName}${args.join("")}`;
    }
    /**获取模型链中被使用的模型 */
    getUsedTypes(types) {
        const base1 = "int,integer,int32,int64";
        const base2 = "number,double,float,decimal,long,short";
        const base3 = "string,boolean,file,blob,array,object";
        const bases = `${base1},${base2},${base3}`.split(",");
        const enums = (this.enums || []).map((x) => x.name);
        return (types || [])
            .filter((x) => !!x.type && !bases.includes(x.type))
            .map((x) => ({
            name: x.type,
            file: x.type,
            isEnum: enums.includes(x.type),
        }));
    }
    /**枚举类型使用兼容处理 */
    fixEnumTypes() {
        const { models, methods } = this;
        //模型属性类型中，枚举类型标记
        models.forEach(({ props }) => (props || []).forEach(({ type }) => (type || []).forEach((x) => this.signEnumType(x))));
        //请求函数中，枚举类型标记
        methods.forEach(({ funcs }) => (funcs || []).forEach(({ inputParms, inputModel, outputModel }) => {
            //传入参数
            (inputParms || []).forEach(({ type }) => (type || []).forEach((x) => this.signEnumType(x)));
            //传入模型
            inputModel.forEach((x) => this.signEnumType(x));
            //返回模型
            outputModel.forEach((x) => this.signEnumType(x));
        }));
    }
    /**标记枚举类型 */
    signEnumType(type) {
        const enums = this.enums || [];
        const typeStr = type.type;
        const desc = type.description || "";
        const enumNames = enums.map((x) => x.name);
        if (!enumNames.includes(typeStr))
            return;
        const entity = (enums.find((x) => x.name === typeStr) || {});
        const enumType = entity.type;
        const items = (entity.props || []).map((x) => x.key).join(",");
        type.isEnum = true;
        type.enumType = enumType;
        type.description = desc ? `${desc}||${items}` : items;
    }
}
exports.default = MParser;
